<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>帮助链接</h1>
语雀:<a href="https://www.yuque.com/about/help">https://www.yuque.com/about/help</a><br>
gitee:<a href="https://gitee.com/help">https://gitee.com/help</a><br>
钉钉:<a href="https://developers.dingtalk.com/">https://developers.dingtalk.com/</a><br>
飞书:<a href="https://open.feishu.cn/document/uQjL04CN/ucDOz4yN4MjL3gzM">https://open.feishu.cn/document</a>

<br>
<br>

    本 科 毕 业 论 文



    消息自动部署系统的设计与实现





    学生姓名：       毕媛媛
    学    号：      201751622
    专    业：   计算机科学与技术
    指导教师：      闫影  讲师




    Suihua University Graduation Paper




    Java-based Tank Wars Online







    Name                Bi Yuanyuan
    Student number        201751622
    Major           Computer Science & Technology
    Supervisor             Yan Ying Teacher




    Suihua University

    摘  要

    许多大型的应用软件需要权限申请，也有许多的文档书写不明确，给应用之间的对接造成了许多的难题，尽量多寻找开源或者免费的应用去对接，形成一个类似于适配器形式，即调即用，并涵盖应用广泛的系统。
    基于webhook（网络钩子技术）通过特定事件，如语雀知识库更新事件、发布事件、评论新增、评论删除等，在飞书客户端群聊通过自定义机器人响应文本、富文本、图片等消息，做到实时提醒大家需求的进展或改动，每个人完成的进度等，或是更好更便捷的分享语雀知识库中的知识资源。
    本课题将通过将多种平台webhook机器人发送与接收消息的功能集成为一个具有消息处理功能的中继器形式的软件以提高工作效率，监控工作进展，增进知识储备交流与分享。
    关键词：webhook；网络钩子；json；轮询


    Abstract
    Many large applications need permission application, and there are many unclear documents, which cause a lot of problems for the docking between applications. Try to find open source or free applications to connect, forming a form similar to adapters, namely ready-to-use, and covering a wide range of applications.
    Hook technology based on webhook (network) by a specific event, such as language knowledge base is updated birds, release events, comment added, deleted, etc., in the flying book client group chat robot through custom response text, rich text, images, news, achieves real-time remind everybody needs progress or changes, each person completed schedule, etc., Or better and more convenient to share the knowledge resources in the knowledge base.
    In this paper, the functions of sending and receiving messages of webhook robots on various platforms are integrated into a software in the form of repeater with message processing function to improve work efficiency, monitor work progress, and enhance the exchange and sharing of knowledge reserve.
    Keywords：webhook ;Network hooks ;json ;polling

    目 录

    摘  要	I
    Abstract	II
    第1章 绪论	1
    1.1 开发消息自动部署系统的原因以及意义	1
    1.2 研究思路与内容	1
    1.3要实现一个什么系统/功能	2
    1.4开发以及研究方法	2
    1.5 涉猎的技术介绍	3
    1.6涉及的平台介绍	5
    第2章 系统设计概要	7
    2.1系统功能模块设计	7
    2.2数据库结构设计	8
    2.3系统界面设计	10
    2.4 消息模板设计	12
    2.5系统流程图	14
    第3章 系统实现细节	16
    3.1后台监控部分	16
    3.2 前台管理部分	17
    结 论	20
    参考文献	21
    致 谢	22
    附 录	23
    第1章 绪 论
    1.1 开发消息自动部署系统的原因以及意义
    在当今的企业中，庞杂的代码和大量的需求经常让开发人员,在本就层出不穷的问题之中分身乏术，而一个能够及时提醒开发人员有何事情需要处理，或是团队成员更新了知识库里面的知识的即使反馈的软件就显得尤为重要。
    Gitee（码云）是开源中国推出的基于git的代码托管协作开发服务平台，支持git和svn，为用户提供了类似github的免费私有仓库托管[1]。Gitee为开发者提供了优秀的本地化、稳定、高效、安全的云端软件开发协作平台，即使是个人或团队还是企业或政府都能够实现代码托管、项目管理、协作开发等优质的服务。
    转眼间大学四年悄然走过，我也成为了一名应届毕业生，所以在公司里面实习是不可避免的，实习之后发现公司里员工再提交代码之后，大家并不知道这个提交动作，如果代码发生了更新修改或删除，相关人员无法及时得知信息，这样不但会造成信息疏漏，严重的还可能会造成项目的延期，系统的紊乱，从而导致严重影响工作效率，于是我就想开发一个消息提醒和部署的项目，把gitee（码云）这种代码管理仓库和语雀这种知识库的消息提醒传递到钉钉或飞书中，方便及时查询，查看。
    提高工作效率，监控工作进展，增进知识储备交流与分享。
    1.2 研究思路与内容
    在初步搭好框架后，整个项目有了一个具体的运行链路，首先要做的是在飞书创建一个自定义机器人，飞书端会生成一个回调url，其他支持webhook的产品通过特定事件触发回调url来实现消息的发送，第二步要做的是把自己项目的地址暴露在外网，让语雀或tapd可以访问，搭建好这些之后在语雀或tapd中模拟消息请求，第三步在程序中先对语雀或tapd发过来的json进行解析，若果是图片则要先down到本地，在上传到飞书服务器，飞书返回一个唯一图片识别码再通过项目中转填入到要发送的json中，通过httpclient.execute(httppost);发送消息。本项目致力于为开发人员提供更好的更优质的更具人性化的消息提醒模板，所以将在后台要对复杂的json进行尽可能细化的分析与提炼，有着丰富多样的通知内容，并且计划实现一个前台管理页面对消息进行管理，建立数据库将以往的通知储存起来，以便于开发人员在后续对历史项目进行故障排查与疏漏分析，权限系统同样也是本系统的一大特色，预计将要实现管理员对用户的分组，并给管理员赋予邮箱实时接收消息的权限。
    1.3要实现一个什么系统/功能
    基于webhook（网络钩子技术）通过特定事件，如语雀知识库更新事件、发布事件、评论新增、评论删除等，tapd如新需求发布、需求进展更新发送等，在飞书客户端群聊通过自定义机器人响应文本、富文本、图片等消息，做到实时提醒大家需求的进展或改动，每个人完成的进度等，或是更好更便捷的分享语雀知识库中的知识资源。
    或如gitee代码仓库的pull动作、推送代码、新建分支、删除分支等功能对代码协作开发平台进行管理，将其消息推送到飞书或钉钉客户端通过自定义机器人推送模板消息，以告知相关开发人员对代码进行处理或撤销等等的操作。
    通过增加适配器的方法来控制不同的应用去对接飞书，希望不只是实现一个桥梁的功能，还具有中继器/适配器的特性特点，做到能随应用的改变，简单配置过后就可以轻松对接。
    在飞书客户端做一些交互式的操作，比如通过@机器人，可以查询一些想得到的知识。
    1.4开发以及研究方法
    文献研究法：通过查阅《设计模式》、《Head First设计模式》、《设计模式之禅》等相关书籍，我发现设计模式在日常编程中对开发效率、代码的简洁程度、高内聚低耦合、甚至是性能方面都起着至关重要的作用，再通读了GOF的《设计模式》后得到了很大的启发，每一种设计模式都有其精妙的构思和设计，于是想要把设计模式应用到软件设计里面。
    实验法：通过项目的构思策划，每一个平台特点加以分析，反复推敲，最终设计出了每一个场景该如何以最人性化的方式发送相应提醒以及将消息模板优化到最佳。
    例如语雀平台发送消息时其实是由许多json串拼凑而成的，而在钉钉飞书等接收方平台无法直接解析杂乱的json，于是就需要我去模拟各种场景下的消息模板，通过对各种平台所需要的各种key进行不断测试、连通最终得到了人性化的消息模板并推送至开发人员的客户端。
    1.5 涉猎的技术介绍
    1.5.1 Springboot框架
    Spring Boot是近些年来不论是企业还是个人java开发者当中炙手可热的一门技术，可以说在当今是否会使用springboot框架是java程序员能否找到工作的一道门槛，它是由Pivotal团队开发的java框架，他们的设计理念是用来对Spring应用、工程的初始搭建以及开发过程的避繁从简。springboot框架使用了非常简便的方式进行配置，从而使开发人员不再需要对繁琐的xml文件进行配置。通过这种方式，Spring Boot已经在java生态中以其完善便捷人性化的配置方式占据了领导者风向标的地位。
    Springboot框架具有控制反转（IOC）特性，IOC旨在方便项目维护和测试，它提供了一种通过Java的反射机制对Java对象进行统一的配置和管理的方法[2]。Springboot同时还具有面向切面编程（AOP）框架，SpringAOP框架基于代理模式，同时运行时可配置；AOP框架主要针对模块之间的交叉关注点进行模块化。其优点或者说特征也是非常的明显
    :（1）可以创建独立Spring应用程序，并且基于其Maven或Gradle插件，可以创建可执行的JARs和WARs；
    （2）内嵌Tomcat或Jetty等Servlet容器；
    （3）提供自动配置的“starter”项目对象模型（POMS）以简化Maven配置；
    （4）尽可能自动配置Spring容器；
    （5）提供准备好的特性，如指标、健康检查和外部化配置；
    （6）绝对没有代码生成，不需要XML配置。
    1.5.2 mysql数据库
    MySQL数据库从属于oracle公司旗下的一款性能强大的轻量级数据库，是当前较为先进的一种数据中心信息管理技术，经过许多企业产品以及时间的考验后证明了它的价值所在，在如今MySQL 数据库已经成为网络存储的核心应用技术之一，在企业中更加被视为是每一位开发者都要掌握的入门级技术[3]。
    MySQL数据库是一种关系型数据库，关系数据库将不同的数据分别保存在多张表中，这样就不但提高了速度和灵活性，而且对数据的管理也非常的井井有条。MySQL所使用的 SQL 语言也是用于访问数据库的最常用标准化规范语言。深受一般中小型网站青睐，大多数网站开发者都会选择 MySQL 作为网站数据库。且由于其开源、本地化、易操作的特点，对于毕业生而言是非常实用的开发工具。
    1.5.3 session
    Session技术在计算机领域被叫做会话控制。其原理是创建一个session用来保存使用者再一次访问中需要的诸如密码、权限等属性与信息[4]。通过如此，在使用者浏览的网页来回跳转的时侯，保存在Session对象中的信息将不会丢失，而是在这个用户保存或登录的过程中一直存活着。每当使用者请求来自浏览器中的网页时，如果该使用者为第一次访问，则Web服务器会自动生成一个 Session对象。当用户注销或账号丢失后，服务器将停止该会话，删除此session。Session 对象最常用的地方就是保存使用者的账号密码等。例如，如果用户登录一个网站后，就可以将该用户的权限等信息存储在Session对象中
    1.5.4 MD5加密
    1992年8月，罗纳德·李维斯特向互联网工程任务组（IETF）提交了一份重要文件，描述了这种算法的原理。由于这种算法的公开性和安全性，在90年代被广泛使用在各种程序语言中，用以确保资料传递无误等。
    MD5由MD4、MD3、MD2改进而来，主要增强算法复杂度和不可逆性。MD5算法因其普遍、稳定、快速的特点，仍广泛应用于普通数据的加密保护领域
    MD5算法的原理可简要的叙述为：MD5码以512位分组来处理输入的信息，且每一分组又被划分为16个32位子分组，经过了一系列的处理后，算法的输出由四个32位分组组成，将这四个32位分组级联后将生成一个128位散列值。每次的运算都由前一轮的128位结果值和当前的512bit值进行运算[5]。
    1.5.5 IDEA
    作为近年来新兴的重量级开发软件，IDEA在公司中发挥的作用举足轻重	，它是jetbrains公司软件开发工具全家桶中的明星软件之一，被认为是全世界上最方便快捷的java开发工具[6]，不但具有齐全的代码智能提示、编译检测、代码重构、J2EE支持、代码自动生成等方便的功能，还相当的开源，为开发者提供了多种多样的便捷插件，支持开发者自行开发的插件发布到开源市场以便分享交流与使用。
    IDEA的系统完善程度甚至达到了可怕的地步，方方面面都透露出IDEA母公司jetbrains中东欧程序员的严谨与负责。然而一款开发工具不仅仅只是需要完善的功能，更要做到符合当今主流开发习惯、语言与框架，将人性化做到极致，而这些也正是IDEA 如此受欢迎的原因。
    1.6涉及的平台介绍
    1.6.1 语雀
    通过几个月的实习，在工作中语雀也成为了相伴我左右必不可少的类开发工具，在我的理解中语雀是一个强大的知识库，不论是在其中发表文章还是查看文章都非常的方便，其强大的markdown文本编辑功能使得撰写博客的时候更加的便捷快速高效，语雀更像是一个社区，可以在群组里面管理、订阅、评论他人的文章，也支持在线协同修改，在实际开发中，项目组经常会把代码格式规范、新人成长路线、注释规范等规范化文档通过语雀分享给开发人员，这不但使得团队协作的效率大大增加，也充分的利用了个人的知识库，将开发人员个人的知识学习模式改变为共享知识学习模式，通过这种方式大大增加了人们的学习效率，从源头上帮助用户建立起知识管理的意识。
    1.6.2 gitee
    该产品隶属于深圳市奥思网络科技有限公司，是2013年基于git推出的代码托管服务平台，也同时是国内最大最方便、本地化极其优秀的一款平台，采用分布式版本库，不需要服务端的支持，使得团队间的代码沟通易如反掌交流与和部署也变得透明，gitee不再仅仅提供 Git 代码托管服务，并且为用户设计了在线查看、历史版本查看、Fork、Pull Request、打包下载任意版本、Issue、Wiki 、保护分支、代码质量检测、PaaS项目演示等方便管理、开发、协作、共享的功能。
    1.6.3 飞书
    在钉钉逐渐占据企业协作平台大部分市场之时，一款设计优美，使用便捷，功能一目了然的产品从众多此类平台中杀出重围，脱颖而出，飞书是字节跳动旗下开发的一款一站式企业协作平台[7]，包含了许多传统功能、在线聊天、记事日历，还推出了许许多多的创新功能如OKR、在线会议、网盘等，是一个整合连通众多功能的大型平台。
    1.6.4 钉钉
    钉钉自不必多说，中国领先的智能移动办公平台，是阿里巴巴旗下开发的免费沟通和协同的多端平台，其好用程度经过许许多多的企业验证，是一款优秀的协作平台[8]。它在疫情爆发的当今社会起到了很大的作用，帮助了大多数学生线上学习的需要，解决了在疫情下学校与学生学习分离的情况，满足了学生求学困难的问题。 
    第2章 系统设计概要
    2.1系统功能模块设计
    本系统的主要功能是为了实现消息的实时提醒，本部分主要分为四个平台的四种消息传输模块，分别为：语雀对飞书实时消息模块、语雀对钉钉实时消息模块、gitee对飞书代码变动提醒模块、gitee对钉钉代码变动提醒模块，其中语雀对飞书实时消息模块可以向飞书客户端添加自定义机器人的群聊中发送更新事件、发布事件、评论新增、评论删除等事件的通知，并且设计了飞书特有的消息模板样式，语雀对钉钉实时消息模块可以向钉钉客户端添加自定义机器人的群聊中发送更新事件、发布事件、评论新增、评论删除等事件的通知，并且设计了专为钉钉定制的消息模板样式，gitee对飞书代码变动提醒模块可以向飞书客户端添加自定义机器人的群聊中发送仓库推送代码、新建、删除分支、新建Tag、删除Tag、新建任务、变更任务状态、更改任务指派人、新建、关闭、合并、关闭PullRequest、新建、更新、删除PullRequest下标签、评论仓库、任务、PullRequest、Commit等事件的通知，并且专为飞书设计了特有的消息模板样式，gitee对钉钉代码变动提醒模块可以向钉钉客户端添加自定义机器人的群聊中发送仓库推送代码、新建、删除分支、新建Tag、删除Tag、新建任务、变更任务状态、更改任务指派人、新建、关闭、合并、关闭PullRequest、新建、更新、删除PullRequest下标签、评论仓库、任务、PullRequest、Commit等事件的通知，并且专为钉钉设计了特有的消息模板样式。，第二个功能是前台监控功能，这里分为俩种权限、第一种管理员的权限可以对消息提醒进行添加、删除、修改、查询的操作，可以赋予普通开发人员管理员的权限、可以对普通开发者进行管理、可以通过邮箱接收到实时消息提醒（与消息模板格式相同），第二种普通开发人员可以对发送的消息提醒历史进行查看，可以修改个人密码。第三个模块是登陆注册功能，本系统中的登陆注册对用户的权限进行了严格的划分，管理员可以随意注册，但注册用户时必须要填写所属的管理员ID，所以没有普通用户管理的管理员不具有任何意义，普通用户被管理员赋予权限后也仅可以对消息提醒做添加、删除、修改、查询操作，而不可以管理普通用户的信息。



    图2-1 系统结构图
    框图文字显示不全
    2.2数据库结构设计
    可以在实体—关系模型的基础上建立数据库结构。实体—关系模型能够非常准确、客观地反映事物特性，同时也能反映出事物与事物的关联。根据实体与其属性之间的联系，每个实体数据都对应一个数据库表，其实体属性与数据库表中各字段相互对应。据此理论，系统建立了如下4个数据库表。

    1.	用户信息表（usermsg）
    用户信息表(usermsg）管理员可以对此表中的信息进行新增，删除，修改，查询，搜索查询，用户可以对此表进行查看，管理员可以将此表生成的消息模板文字版发送到负责人邮箱，此模板文字版保存到提醒事件表，表结构如表3—1所示。

    表2—1 用户信息表（usermsg）
    字段名称	类型	长度	主键	说明
    ID	INT	10	是	用户ID
    PASSWORD	varchar	20	—	用户密码
    USERNAME	varchar	20	—	用户名
    EMAIL	varchar	20	—	用户邮箱
    PHONE	varchar	20	—	用户电话
    COMP	varchar	20	—	公司
    GROUP	varchar	20	—	组名
    PERMISSION	varchar	20	—	权限
    2.语雀信息表（yuque）
    语雀信息表（yuque）用于存储语雀平台发送的json串并解析拆分关键信息至本表管理员可对此表进行管理，表结构如表3—2所示。
    表2—2 语雀信息表（yuque）
    字段名称	类型	长度	主键	说明
    MSGID	int	10	是	消息id
    JSON	longtext	0	—	Json
    EVENT	varchar	20	—	事件
    SPEVENT	varchar	20	—	具体事件
    TRIPEOPLE	varchar	20	—	触发人
    TIME	timestamp	—	—	触发时间
    FROM	varchar	20	—	消息来源
    HEAD
    EMAIL
    BACKUP	varchar
    varchar
    varchar	20
    20
    255	—
    —
    —	组长
    组长邮箱
    备份
    3.gitee信息表（gitee）
    gitee信息表（gitee）区别于语雀反映的是代码库的各种提交动作与反馈，表结构如表2—3所示。
    表2—3 gitee信息表（gitee）
    字段名称	类型	长度	主键	说明
    MSGID	int	10	是	信息id
    JSON	Longtext	0	—	博客标签
    DESC
    ACTION
    PERSON
    TIME
    SOURCE
    MANAGER
    EMAIL
    BACKUP	varchar
    varchar
    varchar
    timestamp
    varchar
    varchar
    varchar
    varchar	20
    20
    20
    —
    20
    20
    20
    255	—
    —
    —
    —
    —
    —
    —
    —	事件描述
    动作
    提交人
    提交时间
    来源
    项目负责人
    负责人邮箱
    备份

    4.事件提醒表（remind）
    事件提醒表（remind）当管理员给负责人发送事件到邮箱时，会自动保存一份消息到本表统计提醒事件内容、提醒事件状态、负责人、发送人，发送时间
    ，表结构如表2—4所示。
    表2—4 事件提醒表（remind）
    字段名称	类型	长度	主键	说明
    MSGID	int	10	是	事件id
    CONTENT	varchar	20	—	用户姓名
    HEAD	varchar	20	—	负责人
    SENDER	varcahr	20	—	发送人
    TIME	timestamp	—	—	发送时间

    2.3系统界面设计
    2.3.1 用户界面
    （1）用户注册、登录
    图2-2 用户界面
    （2）首页
    图2-3 用户首页
    （3）个人中心
    图2-4 用户个人中心
    （4）帮助
    图2-5 用户帮助
    2.3.2 管理员界面
    （1）管理员登录
    图2-6 管理员登录
    （2）首页
    图2-7 首页
    （3）用户管理
    图2-8 用户管理
    （4）个人中心
    图2-9 个人中心
    （5）提醒事件管理
    图2-10 提醒事件管理
    内容不完整！！
    2.4 消息模板设计
    2.4.1 语雀事件

    图2-11 语雀更新消息模板-钉钉

    图2-12 语雀更新消息模板-飞书
    2.4.2 gitee事件


    图2-13 gitee更新消息模板
    2.5系统流程图
    2.5.1 用户流程图

    图2-14 用户流程图
    文字显示不全，且没有流程图说明
    2.5.2 管理员流程图

    图2-15 管理员流程图
    第3章 系统实现细节
    3.1后台监控部分
    3.1.1 语雀对飞书实时消息模块
    在语雀触发知识库更新、删除、创建等事件后发送json消息至本系统，并在后台进行处理，转至飞书中添加了自定义机器人的群聊中

    图3-1 语雀-飞书后台json解析过程
    3.1.2 语雀对钉钉实时消息模块
    图片文字不清晰
    在语雀触发知识库更新、删除、创建等事件后发送json消息至本系统，并在后台进行处理，转至钉钉中添加了自定义机器人的群聊中

    图3-2 语雀-钉钉后台json解析过程

    3.1.3 gitee对飞书代码变动提醒模块
    在gitee触发代码仓库提交代码、创建分支、删除分支等事件后发送json消息至本系统，并在后台进行处理，转至飞书中添加了自定义机器人的群聊中

    图3-3 gitee-飞书后台json解析过程

    3.1.4 gitee对钉钉代码变动提醒模块
    在gitee触发代码仓库提交代码、创建分支、删除分支等事件后发送json消息至本系统，并在后台进行处理，转至钉钉中添加了自定义机器人的群聊中

    图3-4 gitee-钉钉后台json解析过程
    3.2 前台管理部分
    3.2.1 用户界面
    （1）用户注册、登录
    用户注册完毕后会有一封邮件发送到邮箱，此邮件包含用户新注册的用户名和密码，同时登录信息会发送至管理员的邮箱中，方便管理员进行管理，这里发送邮件操作需要通过在邮箱中开启POP/SMTP服务并在代码中调用java自带的emailAPI实现，使得用户的账户安全得到足够的保障。
    （2）首页
    首页主要向用户展示了语雀、gitee向飞书钉钉平台发送的消息的详细信息等，用户只有查看和搜索权限而不能对信息进行修改以保证历史消息记录的真实和准确性，这里主要通过在语雀和gitee中开启webhook服务，触发人触发特定事件，在将gitee与语雀中的消息拆解拼接组装优化后存入数据库并发送至飞书钉钉，方便用户进行查看。
    （3）个人中心
    用户的个人中心可以展示用户的详细信息如用户名、密码、手机号邮箱等信息，如详细信息不完整可以点击完善信息进行补全、还可以修改密码、注销，修改密码会通过邮件的方式用算法生成随机密码，以防止密码泄露，其中用户名密码和邮箱为登陆必填项，管理员会通知消息到特定用户的邮箱之中，手机号、公司、组名为选填项，其具体功能尚未开发。
    （4）帮助
    帮助部分是专为用户设计的一部分内容，包含本平台的用法，如何将此系统进行实装使用，以及提供语雀、gitee、飞书、钉钉等平台的开发文档以供查看，方便开发者快速上手使用。
    3.2.2 管理员界面
    （1）管理员登录
    管理员登陆唯一的密码账号，不可外泄，登录后会在邮箱收到登录提示，也可以收到用户的登陆提醒，这些提醒也会一一存储到数据库中。
    （2）首页
    管理员的首页可以增加、修改、删除、查询其他平台发送来的消息，可以点击发送消息按钮将自动生成的模板消息发送到项目负责人的邮箱，可重复发送，如负责人已完成目标，则可以将状态标记为已读。
    （3）用户管理
    用户管理可以把注册进来的用户进行管理，对用户信息进行修改删除、可以增加新用户，展示用户的用户名密码等详细信息，可通过邮箱发送信息通知到用户。
    （4）个人中心
    可以展示管理员的详细信息，可以帮助用户完善个人信息
    （5）提醒事件管理
    管理员可以查看发送的邮箱通知，查看提醒内容、负责人、发送人、发送时间等信息，也可以查看用户的登录信息，以及对这些记录的修改和删除操作。
    整章缺少运行结果的测试图片及说明 
    结 论
    经过了三个月的作品设计与论文编写后，我获得了许许多多的感受，一个完整的软件产品开发是多么的不易，从最初期的作品准备期开始取材，定题开始就要花费很多心思去观察判断和总结，在日常工作中有哪些不够便捷的点，在开发过程中容易出现那些疏漏，这也使我下定决心去完成一个如此不同的项目，不同于其他的管理系统，我想开发出的并不是具有美观的界面，增删改查等操作庞杂乏味的统计管理类软件，而是一个真正能由于工作中，重视后台消息分析，重视对消息的处理分发能力，多平台连通的中继器系统。这要求我要从实际问题出发，在开发时要切合开发人员的实际使用习惯与关键消息获取，将工作与生活同时融入代码，这也使我对编程的兴趣更加的浓厚，比以往又多了一份热爱。
    本系统使用了当今大热的webhook技术，摒弃以往软件或网站后台轮询的方式，通过回调将即时消息及时发送到协作平台，不但节省了大量的服务器性能资源，同时也大大的加快了项目组中的开发效率与管理能力，我将大量的时间花在了json解析与消息模板设计上，这样的优点在于摒弃了不需要的json消息，同时再存入数据库的时候数据井井有条，可以让管理者在排查故障的时候能够迅速定位到负责人身上并解决问题。由于时间原因，这款软件目前只整合了四个平台，在未来的开发周期中还会不断的迭代，连接更多的平台，让大多数的平台与平台之间实现互通、简化配置手段，让使用者能够迅速部署到开发环境中去、优化模板，让开发者能够看到更加美观的模板，迅速获取有用的信息。
    当然本系统还有许多待完善和不足之处，例如对接的平台数量不够多、模板设计不是太美观，信息利用率有待提高，前台管理没有和钉钉飞书等平台群组系统对接，与用户的交互操作等等，在未来的版本中这些都会持续开发跟进。
    通过完成这次的毕业设计，我得到了前所未有的成长，也激发了我的编程兴趣，在未来的工作成长中我相信还会设计出更好更完整更加实用的软件。
     
    参考文献
    [1]	Ling Wang,Jinxiao Wang. The Impact of Project Initiators on Open Source Software Project Success: Evidence From Emerging Hosting Platform Gitee[J]. International Journal of Information Systems in the Service Sector (IJISSS),2020,12(1).
    [2]	江健锋,徐振平.Springboot最小系统的设计与实现[J].电脑知识与技术,2021,17(04):62-63.
    [3]	盛莉.基于MySQL的数据管理平台设计[J].信息与电脑(理论版),2020,32(22):152-153.
    [4]	2021 WHS Abstract Session List Key.[J]. Wound repair and regeneration : official publication of the Wound Healing Society [and] the European Tissue Repair Society,2021,29(3).
    [5]	刘倩,吕艳娜.基于MD5的IP城域网动态IGP协议加密部署[J].信息通信,2019(12):216-217.
    [6]	李可.Java开发利器——IntelliJ IDEA[J].程序员,2003(02):107-108.
    [7]	李佳.《飞书》在线办公风生水起[J].计算机与网络,2020,46(06):36.
    [8]	缪沁男,魏江,杨升曦.服务型数字平台的赋能机制演化研究 ——基于钉钉的案例分析[J/OL].科学学研究:1-17[2021-05-25].https://doi.org/10.16192/j.cnki.1003-2053.20210507.004.
    [9]	.
    [10]	.
     
    致 谢
    岁月如梭，时间如白驹过隙，转瞬即逝，四年的大学生涯即将迎来句号，从军训月到毕业季，四年的时间剪影历历在目，从刚刚见到新老师，新室友，新同学，到最后的依依不舍。四年时光正因为有了你们，我的大学生活才能变得非常精彩。在毕业设计结束后，我们就真的要告别我们的大学生活，正式踏入社会工作了，在此我想对给予我帮助的大学老师和同学们表示我由衷的感谢，谢谢你们丰富了我的大学生活。
    感谢所有的任课教师们无私的传授知识与耐心的教导，也非常感谢我的指导老师闫影老师对我的帮助，无论是在实习期间还是毕业设计制作期间，是闫影老师不厌其烦地帮我渡过各种难关，即使是在外省实习时，老师依然无比耐心的为我操劳毕业的相关事宜。我的这次毕业设计就是在闫影老师的悉心指导下完成的。感谢老师从开题、中期检查到毕业设计的几个阶段的悉心指导和建议，让我能按时完成毕业设计，为我的四年大学生涯画上完美的句号。在此，我向我的导师表示浓浓的感激与深深的敬意！
    感谢这四年大学里学校对我的栽培，感谢母校的教育之恩。感谢学院提供给我的教育环境，让我学到许多新的知识，感谢辅导员老师的无私奉献与温暖关怀，即使远在他乡亦能感受亲情所在，感谢各位老师的悉心教导让我今后在工作中能有一技之长。感谢同学们在生活与学习上对我的各种帮助，四年来是你们陪我走过，这四年是美好的青春，是人生的成长，是不舍的别离，是难忘的回忆。
    在调试程序过程中遇到了诸多不曾遇到的问题，因自己的知识有限不能很好的调试系统，是我的同学和老师耐心讲解，帮助我很好的完成了最后的设计，让我及时地完成了我的系统。
    感谢这四年一起学习成长的朋友，感谢曾经帮助过我的老师和同学，这四年来有你们在我身边是我最大的幸运。谢谢你们！
     
    附 录
    WebhookController
    说明：此类包含了四个后台模块的接口
    package com.lwz.automsg.controller;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.dingtalk.api.DefaultDingTalkClient;
    import com.dingtalk.api.DingTalkClient;
    import com.dingtalk.api.request.OapiRobotSendRequest;
    import com.dingtalk.api.response.OapiRobotSendResponse;
    import com.lwz.automsg.service.SelectDingTemp;
    import com.lwz.automsg.service.SelectTemp;
    import com.lwz.automsg.utils.*;
    import com.taobao.api.ApiException;
    import org.apache.http.HttpResponse;
    import org.apache.http.HttpStatus;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Properties;
    import java.util.UUID;
    @Controller
    public class WebhookController {
    @Autowired
    ChangePathUtil changePathUtil;
    @Autowired
    SendImageUtil sendImageUtil;
    @Autowired
    GetChatIdUtil getChatIdUtil;
    @Autowired
    SelectTemp selectTemp;
    @Autowired
    SelectDingTemp selectDingTemp;
    /**
    * 语雀-飞书
    */
    @PostMapping("/yuque")
    public String redirect(@RequestBody Object req) throws IOException {
    String WEBHOOK_TOKEN = "https://open.feishu.cn/open-apis/bot/v2/hook/d2659769-1583-4b6e-97be-fce221e40668";
    HttpClient httpclient = HttpClients.createDefault();
    HttpPost httppost = new HttpPost(WEBHOOK_TOKEN);
    httppost.addHeader("Content-Type", "application/json; charset=utf-8");
    String key_word="项目更新";
    String s = JacksonUtil.toJSON(req);
    System.out.println(s);
    //获取"result"中的json字符串
    JSONObject parse = (JSONObject) JSON.parse(s);
    //获取字符数组
    String data = parse.getString("data");
    JSONObject dataParse = (JSONObject) JSON.parse(data);
    String book = dataParse.getString("book");
    JSONObject bookParse = (JSONObject) JSON.parse(book);
    String user = dataParse.getString("user");
    JSONObject userParse = (JSONObject) JSON.parse(user);
    String body_html = dataParse.getString("body_html");
    String description = bookParse.getString("description");
    String bookTitle = bookParse.getString("name");
    String title = dataParse.getString("title");
    String name = userParse.getString("name");
    String url = dataParse.getString("path");
    String body = dataParse.getString("body_html");
    Properties prop = System.getProperties();
    String os = prop.getProperty("os.name");
    ResolveUrlUtil resolveUrlUtil = new ResolveUrlUtil();
    List img = resolveUrlUtil.imgs(body_html);
    UUID uuid=null;
    String imgName="";
    for(String imgUrl:img){
    //用uuid来生成随机文件名
    uuid = UUID.randomUUID();
    imgName = uuid.toString();
    //将文件down到本地
    if (os != null && os.toLowerCase().contains("linux")) {
    DownImageUtil.downloadImg(imgUrl,
    changePathUtil.linuxFilePath,
    imgName +".jpg");
    System.out.println(imgUrl);
    } else {
    DownImageUtil.downloadImg(imgUrl,
    changePathUtil.windowsFilePath,
    imgName +".jpg");
    System.out.println(imgUrl);
    }

    }
    //获取飞书端imgKey
    String imgKey = sendImageUtil.sendImg(imgName);
    System.out.println(imgKey);

    //构建一个json格式字符串textMsg，其内容是接收方需要的参数和消息内容
    String richTextMsg=selectTemp.tempAdapter(s,imgKey);
    StringEntity se = new StringEntity(richTextMsg, "utf-8");
    httppost.setEntity(se);
    System.out.println(richTextMsg);
    HttpResponse response = httpclient.execute(httppost);
    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
    String result = EntityUtils.toString(response.getEntity(), "utf-8");
    System.out.println(result);
    }
    return "hello";
    }
    @RequestMapping("/test")
    public String test(){
    System.out.println("这是一个测试！");
    return "hello";
    }
    @RequestMapping("getOS")
    public void osName(){
    Properties prop = System.getProperties();
    String os = prop.getProperty("os.name");
    System.out.println(os);
    }
    /**
    * tapd-飞书接口
    */
    @PostMapping("/tapd")
    public String redirectTAPD(@RequestBody Object req) throws IOException {
    String URL = "https://api.tapd.cn/quickstart/testauth";
    HttpClient httpclient = HttpClients.createDefault();
    HttpGet request = new HttpGet(URL);
    request.addHeader("Content-Type", "application/json; charset=utf-8");
    request.addHeader("Authorization","Basic RnFLV1ZjM1Q6MTI2RkYzMDAtM0JGNS1FRDU3LTVCM0EtRjUyRkJCQzM0M0Ex");
    String key_word="项目更新";
    String s = JacksonUtil.toJSON(req);
    System.out.println(s);
    HttpResponse execute = httpclient.execute(request);
    System.out.println(EntityUtils.toString(execute.getEntity(),"UTF-8"));
    return "";
    }
    /**
    * 语雀- 钉钉
    * */
    @PostMapping("/yuqueding")
    public String yuQueDing(@RequestBody Object req) throws ApiException, IOException {
    String s = JacksonUtil.toJSON(req);
    System.out.println(s);
    DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?access_token=a6e4cf7618bbd1361dce83ee238b194e624527be349a0730634572bc5c551ae7");
    OapiRobotSendRequest request;
    request = selectDingTemp.tempAdapterForDing(s);
    OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
    at.setAtMobiles(Arrays.asList("18846558370"));
    at.setIsAtAll(false);
    request.setAt(at);
    System.out.println("发送消息！");
    OapiRobotSendResponse response = client.execute(request);
    return "";
    }
    /**
    * gitee- 钉钉
    * */
    @PostMapping("/gitee")
    @ResponseBody
    public String gitee(@RequestBody Object req) throws IOException, ApiException {
    String s = JacksonUtil.toJSON(req);
    System.out.println(s);
    DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?access_token=a6e4cf7618bbd1361dce83ee238b194e624527be349a0730634572bc5c551ae7");
    OapiRobotSendRequest request;
    request = selectDingTemp.tempAdapterForGitToDing(s);
    OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
    at.setAtMobiles(Arrays.asList("18846558370"));
    at.setIsAtAll(false);
    request.setAt(at);
    System.out.println("发送消息！");
    OapiRobotSendResponse response = client.execute(request);
    return "";
    }
    /**
    * gitee- 飞书
    */
    @PostMapping("/gitfeishu")
    public String giteeToDing(@RequestBody Object req) throws IOException {
    System.out.println("一条请求进入方法");
    String WEBHOOK_TOKEN = "https://open.feishu.cn/open-apis/bot/v2/hook/d2659769-1583-4b6e-97be-fce221e40668";
    HttpClient httpclient = HttpClients.createDefault();
    HttpPost httppost = new HttpPost(WEBHOOK_TOKEN);
    httppost.addHeader("Content-Type", "application/json; charset=utf-8");
    String s = JacksonUtil.toJSON(req);
    System.out.println(s);
    //构建一个json格式字符串textMsg，其内容是接收方需要的参数和消息内容
    String richTextMsg=selectDingTemp.richTextToFeishu(s,getChatIdUtil.getChatId());
    StringEntity se = new StringEntity(richTextMsg, "utf-8");
    httppost.setEntity(se);
    System.out.println(richTextMsg);
    HttpResponse response = httpclient.execute(httppost);
    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
    String result = EntityUtils.toString(response.getEntity(), "utf-8");
    System.out.println(result);
    }
    return "hello";
    }
    }
    SelectTemp
    说明：此类为语雀的json解析类service层

    package com.lwz.automsg.service;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.lwz.automsg.DTO.*;
    import com.lwz.automsg.enumClass.JsonTemp;
    import com.lwz.automsg.utils.GetChatIdUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import java.io.IOException;
    /**
    * @author lwz
    */
    @Service
    public class SelectTemp {
    @Autowired
    JsonTemp jsonTemp;
    @Autowired
    GetChatIdUtil getChatIdUtil;
    /**
    * 抽取常用的第一层jsonKey减少代码冗余
    */
    private JSONObject getJson(String json){
    return  (JSONObject) JSON.parse(((JSONObject) JSON.parse(json)).getString("data"));
    }
    /**
    * @param json controller层传的第一层jsonKey,消息适配器
    */
    public String tempAdapter(String json,String imgKey) throws IOException {
    if (getJson(json).getString("published_at").equals(getJson(json).getString("first_published_at"))) {
    System.out.println("事件：首次发布，创建文档，文案：xxx公开了新的知识");
    //返回jsonTemp中的模板
    return jsonTemp.createNoticeCard(jsonToBean(getJson(json),imgKey));
    }else if("delete".equals(getJson(json).getString("action_type"))){
    return jsonTemp.deleteNoticeCard(jsonToBean(getJson(json),imgKey));
    }else if("update".equals(getJson(json).getString("action_type"))){
    return jsonTemp.updateNoticeCard(jsonToBean(getJson(json),imgKey));
    }
    return "";
    }

    //:todo 封装json
    public JsonDTO jsonToBean(JSONObject jsonObject,String imgKey) throws IOException {
    JsonDTO jsonDTO = new JsonDTO();
    DataDTO dataDTO = new DataDTO();
    RootDTO rootDTO = new RootDTO();
    UserDTO userDTO = new UserDTO();
    BookDTO bookDTO = new BookDTO();
    String title = jsonObject.getString("title");
    String description = jsonObject.getString("description");
    String user = jsonObject.getString("user");
    String bodyHtml = jsonObject.getString("bodyHtml");
    JSONObject userParse = (JSONObject)JSONObject.parse(user);
    String name = userParse.getString("name");
    String path = jsonObject.getString("path");
    userDTO.setName(name);
    dataDTO.setTitle(title);
    dataDTO.setPath(path);
    bookDTO.setDescription(description);
    dataDTO.setBodyHtml(bodyHtml);
    jsonDTO.setCharId(getChatIdUtil.getChatId());
    jsonDTO.setImageKey(imgKey);
    jsonDTO.setBookDTO(bookDTO);
    jsonDTO.setDataDTO(dataDTO);
    jsonDTO.setRootDTO(rootDTO);
    jsonDTO.setUserDTO(userDTO);
    return jsonDTO;
    }
    }
    giteeMsgHandler
    说明：此类为钉钉的消息模板处理类
    package com.lwz.automsg.service;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    import com.lwz.automsg.DTO.GiteeDTO;
    import org.springframework.stereotype.Service;


    @Service
    public class giteeMsgHandler {

    //抽取方法
    private JSONObject getJson(String json){
    //commits中的信息
    String commits = null;
    try {
    commits = (((JSONObject) JSON.parse(json)).getString("commits"));
    } catch (Exception e) {
    commits = (((JSONObject) JSON.parse(json)).getString("head_commits"));
    }
    if(((JSONArray) JSONArray.parse(commits)).size()==0){
    return null;
    }
    return  (JSONObject) ((JSONArray) JSONArray.parse(commits)).get(0);
    }
    //时间、人物、事件、级别、url
    public GiteeDTO gitToDingHandler(String json){
    GiteeDTO giteeDTO = new GiteeDTO();
    //项目名
    String projectName = ((JSONObject) JSON.parse(json)).getJSONObject("project").getString("name");
    //项目所有者
    String owner = ((JSONObject) JSON.parse(json)).getJSONObject("project").getJSONObject("owner").getString("name");
    //提交分支
    String branch = ((JSONObject) JSON.parse(json)).getJSONObject("project").getString("default_branch");
    if(getJson(json)!=null) {
    //提交信息
    String message = getJson(json).getString("message");
    //提交时间
    String timestamp = getJson(json).getString("timestamp");
    //url
    String url = getJson(json).getString("url");
    //author的Json串-提交人
    String name = (getJson(json).getJSONObject("committer")).getString("name");
    //email
    String email = (getJson(json).getJSONObject("committer")).getString("email");
    //修改了哪里（path）
    String modified = getJson(json).getString("modified");
    giteeDTO.setEmail(email);
    giteeDTO.setMessage(message);
    giteeDTO.setModified(modified);
    giteeDTO.setOwner(owner);
    giteeDTO.setName(name);
    giteeDTO.setTimestamp(timestamp);
    giteeDTO.setUrl(url);
    }
    giteeDTO.setBranch(branch);
    giteeDTO.setProjectName(projectName);
    return giteeDTO;
    }
    }



</body>
</html>